package daybook.io.formatter;

import daybook.model.Account;
import daybook.model.Book;
import daybook.model.Commodity;
import daybook.model.transaction.InventoryEntry;
import daybook.model.transaction.LedgerEntry;
import daybook.model.transaction.Transaction;
import daybook.model.transaction.TransactionSummaryStatistics;
import daybook.util.Pair;
import daybook.util.tree.Tree;

import java.io.*;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Stream;

/**
 * Created by crc on 16-9-1.
 */
public interface Formatter {
    Format getFormat();

    // write to writer
    void writeLedger(Map<Account, List<LedgerEntry>> ledger, Writer writer);
    void writeBalanceSheet(NavigableMap<LocalDate, Tree<Pair<Account, BigDecimal>>> balanceSheets, LocalDate startDate, Writer writer);
    void writeIncomeStatement(NavigableMap<LocalDate, Tree<Pair<Account, BigDecimal>>> incomeStatements, LocalDate startDate, Writer writer);
    void writeCashFlow(NavigableMap<LocalDate, Map<Boolean, Map<Account, BigDecimal[]>>> cashFlows,
                       LocalDate startDate, Set<Account> cashAccounts, Writer writer);
    void writeInventory(Map<Commodity, List<InventoryEntry>> inventory, Writer writer);
    void writeStatistics(TransactionSummaryStatistics statistics, Writer writer);
    void writeBook(Book book, Writer writer);
    void writeTransactions(Stream<Transaction> transactions, Writer writer);
    void writeCollection(Collection<?> ss, Writer writer);

    // write to file
    default void writeLedger(Map<Account, List<LedgerEntry>> ledger, Path path, String filename) {
        final Path file = path.resolve(getFormat().appendFileExtension(filename, false));
        try (final Writer writer = Files.newBufferedWriter(file)) {
            writeLedger(ledger, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void writeBalanceSheet(NavigableMap<LocalDate, Tree<Pair<Account, BigDecimal>>> balanceSheets, LocalDate startDate, Path path, String filename) {
        final Path file = path.resolve(getFormat().appendFileExtension(filename, false));
        try (final Writer writer = Files.newBufferedWriter(file)) {
            writeBalanceSheet(balanceSheets, startDate, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void writeIncomeStatement(NavigableMap<LocalDate, Tree<Pair<Account, BigDecimal>>> incomeStatements, LocalDate startDate, Path path, String filename) {
        final Path file = path.resolve(getFormat().appendFileExtension(filename, false));
        try (final Writer writer = Files.newBufferedWriter(file)) {
            writeIncomeStatement(incomeStatements, startDate, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void writeCashFlow(NavigableMap<LocalDate, Map<Boolean, Map<Account, BigDecimal[]>>> cashFlows,
                       LocalDate startDate, Set<Account> cashAccounts, Path path, String filename) {
        final Path file = path.resolve(getFormat().appendFileExtension(filename, false));
        try (final Writer writer = Files.newBufferedWriter(file)) {
            writeCashFlow(cashFlows, startDate, cashAccounts, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void writeInventory(Map<Commodity, List<InventoryEntry>> inventory, Path path, String filename) {
        final Path file = path.resolve(getFormat().appendFileExtension(filename, false));
        try (final Writer writer = Files.newBufferedWriter(file)) {
            writeInventory(inventory, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void writeStatistics(TransactionSummaryStatistics statistics, Path path, String filename) {
        final Path file = path.resolve(getFormat().appendFileExtension(filename, false));
        try (final Writer writer = Files.newBufferedWriter(file)) {
            writeStatistics(statistics, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void writeBook(Book book, Path path, String filename) {
        final Path file = path.resolve(getFormat().appendFileExtension(filename, false));
        try (final Writer writer = Files.newBufferedWriter(file)) {
            writeBook(book, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void writeTransactions(Stream<Transaction> transactions, Path path, String filename) {
        final Path file = path.resolve(getFormat().appendFileExtension(filename, false));
        try (final Writer writer = Files.newBufferedWriter(file)) {
            writeTransactions(transactions, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void writeCollection(Collection<?> ss, Path path, String filename) {
        final Path file = path.resolve(getFormat().appendFileExtension(filename, false));
        try (final Writer writer = Files.newBufferedWriter(file)) {
            writeCollection(ss, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    // write to System.out
    default void printLedger(Map<Account, List<LedgerEntry>> ledger) {
        try (final Writer writer = new BufferedWriter(new OutputStreamWriter(System.out))) {
            writeLedger(ledger, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void printBalanceSheet(NavigableMap<LocalDate, Tree<Pair<Account, BigDecimal>>> balanceSheets, LocalDate startDate) {
        try (final Writer writer = new BufferedWriter(new OutputStreamWriter(System.out))) {
            writeBalanceSheet(balanceSheets, startDate, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void printIncomeStatement(NavigableMap<LocalDate, Tree<Pair<Account, BigDecimal>>> incomeStatements, LocalDate startDate) {
        try (final Writer writer = new BufferedWriter(new OutputStreamWriter(System.out))) {
            writeIncomeStatement(incomeStatements, startDate, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void printCashFlow(NavigableMap<LocalDate, Map<Boolean, Map<Account, BigDecimal[]>>> cashFlows,
                       LocalDate startDate, Set<Account> cashAccounts) {
        try (final Writer writer = new BufferedWriter(new OutputStreamWriter(System.out))) {
            writeCashFlow(cashFlows, startDate, cashAccounts, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void printInventory(Map<Commodity, List<InventoryEntry>> inventory) {
        try (final Writer writer = new BufferedWriter(new OutputStreamWriter(System.out))) {
            writeInventory(inventory, writer);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void printStatistics(TransactionSummaryStatistics statistics) {
        try (final Writer writer = new BufferedWriter(new OutputStreamWriter(System.out))) {
            writeStatistics(statistics, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void printBook(Book book) {
        try (final Writer writer = new BufferedWriter(new OutputStreamWriter(System.out))) {
            writeBook(book, writer);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void printTransactions(Stream<Transaction> transactions) {
        try (final Writer writer = new BufferedWriter(new OutputStreamWriter(System.out))) {
            writeTransactions(transactions, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    default void printCollection(Collection<?> ss) {
        try (final Writer writer = new BufferedWriter(new OutputStreamWriter(System.out))) {
            writeCollection(ss, writer);
            writer.flush();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
}
